    // Copyright (C) 2005  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_CMD_LINE_PARSER_PRINt_1_
    #define DLIB_CMD_LINE_PARSER_PRINt_1_

    #include "cmd_line_parser_kernel_abstract.h"
    #include "../algs.h"
    #include "../string.h"
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <map>
    #include <memory>

    namespace dlib
    {

        template <
            typename clp_base
            >
        class cmd_line_parser_print_1 : public clp_base
        {

            public:

                void print_options (
                    std::basic_ostream<typename clp_base::char_type>& out
                ) const;

                void print_options (
                ) const
                {
                    print_options(std::cout);
                }

        };

        template <
            typename clp_base
            >
        inline void swap (
            cmd_line_parser_print_1<clp_base>& a,
            cmd_line_parser_print_1<clp_base>& b
        ) { a.swap(b); }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename clp_base
            >
        void cmd_line_parser_print_1<clp_base>::
        print_options (
            std::basic_ostream<typename clp_base::char_type>& out
        ) const
        {
            typedef typename clp_base::char_type ct;
            typedef std::basic_string<ct> string;
            typedef typename string::size_type size_type;

            typedef std::basic_ostringstream<ct> ostringstream;

            try
            {

                size_type max_len = 0;
                this->reset();

                // this loop here is just the bottom loop but without the print statements.
                // I'm doing this to figure out what len should be.
                while (this->move_next())
                {
                    size_type len = 0;
                    len += 3;
                    if (this->element().name().size() > 1)
                    {
                        ++len;
                    }
                    len += this->element().name().size();

                    if (this->element().number_of_arguments() == 1)
                    {
                        len += 6;
                    }
                    else
                    {
                        for (unsigned long i = 0; i < this->element().number_of_arguments(); ++i)
                        {
                            len += 7;
                            if (i+1 > 9)
                                ++len;
                        }
                    }

                    len += 3;
                    if (len < 33)
                        max_len = std::max(max_len,len);
                }

                // Make a separate ostringstream for each option group.  We are going to write
                // the output for each group to a separate ostringstream so that we can keep
                // them grouped together in the final output.
                std::map<string,std::shared_ptr<ostringstream> > groups;
                this->reset();
                while(this->move_next())
                {
                    if (!groups[this->element().group_name()])
                        groups[this->element().group_name()].reset(new ostringstream);
                }

                this->reset();

                while (this->move_next())
                {
                    ostringstream& sout = *groups[this->element().group_name()];

                    size_type len = 0;
                    sout << _dT(ct,"\n  -");
                    len += 3;
                    if (this->element().name().size() > 1)
                    {
                        sout << _dT(ct,"-");
                        ++len;
                    }
                    sout << this->element().name();
                    len += this->element().name().size();

                    if (this->element().number_of_arguments() == 1)
                    {
                        sout << _dT(ct," <arg>");
                        len += 6;
                    }
                    else
                    {
                        for (unsigned long i = 0; i < this->element().number_of_arguments(); ++i)
                        {
                            sout << _dT(ct," <arg") << i+1 << _dT(ct,">");
                            len += 7;
                            if (i+1 > 9)
                                ++len;
                        }
                    }

                    sout << _dT(ct,"   ");
                    len += 3;

                    while (len < max_len)
                    {
                        ++len;
                        sout << _dT(ct," ");
                    }

                    const unsigned long ml = static_cast<unsigned long>(max_len);
                    // now print the description but make it wrap around nicely if it
                    // is to long to fit on one line.
                    if (len <= max_len)
                        sout << wrap_string(this->element().description(),0,ml);
                    else
                        sout << _dT(ct,"\n") << wrap_string(this->element().description(),ml,ml);
                }

                // Only print out a generic Options: group name if there is an unnamed option
                // present.
                if (groups.count(string()) == 1)
                    out << _dT(ct,"Options:");

                // Now print everything out
                typename std::map<string,std::shared_ptr<ostringstream> >::iterator i;
                for (i = groups.begin(); i != groups.end(); ++i)
                {
                    // print the group name if we have one
                    if (i->first.size() != 0)
                    {
                        if (i != groups.begin())
                            out << _dT(ct,"\n\n");
                        out << i->first << _dT(ct,":");
                    }

                    // print the options in the group
                    out << i->second->str();
                }
                out << _dT(ct,"\n\n");
                this->reset();
            }
            catch (...)
            {
                this->reset();
                throw;
            }
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_CMD_LINE_PARSER_PRINt_1_